﻿using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;

namespace CustomAuthorization
{
    class Program
    {
        const string urlRoot = "http://localhost:52466";
        const string urlLogin = "api/Auth/Login";
        const string urlRefreshToken = "api/Auth/Refresh?token={0}";
        const string urlCurrentUser = "api/Test/CurrentUser";
        const string urlGet = "api/Test/{0}";
        const string urlCreate = "api/Test/Create";
        const string urlUpdate = "api/Test/Update";
        const string urlPatch = "api/Test/Patch";
        const string urlDelete = "api/Test/{0}";

        class User
        {
            [Newtonsoft.Json.JsonIgnore]
            internal int Id;
            public string UserName { get; set; }
            public string Password { get; set; }
            internal User(int id, string name, string pw) { Id = id; UserName = name; Password = pw; }
        }

        private static readonly List<User> users = new List<User>
        {
            new User(1, "admin", "123456"),
            new User(2, "user", "123" ),
            new User(3, "tester", "123")
        };


        static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("请选择：");
                Console.WriteLine("A) 测试身份验证");
                Console.WriteLine("B) 测试权限鉴定");
                Console.WriteLine("Q) 退出");
                var s = Console.ReadLine();
                if (s == "A" || s == "a")
                {
                    TestAuthentication();
                }
                else if (s == "B" || s == "b")
                {
                    TestAuthorization();
                }
                else if(s=="Q" || s=="q")
                {
                    break;
                }
            }
        }

        private static void TestAuthentication()
        {
            var user = SelectUser();
            if (user == null)
            {
                using (var client = CreateHttpClient())
                {
                    Console.WriteLine($"将以未登录用户请求[{urlCurrentUser}]");
                    var res = client.GetAsync(urlCurrentUser).Result;
                    res.EnsureSuccessStatusCode();
                    Console.WriteLine($"返回状态：{res.StatusCode}，数据：" + res.Content.ReadAsStringAsync().Result);
                }
                return;
            }
            var token = TestLogin(user);
            if (token != null)
                Console.WriteLine($"用户\"{user.UserName}\"登录成功，访问令牌：{token.AccessToken}");
            else
                Console.WriteLine($"用户\"{user.UserName}\"登录失败！");
            using (var client = CreateHttpClient(token.AccessToken))
            {
                int i = 0, errors = 0;
                var interactive = false; //是否交互式(即每次发送需要人工按键)
                Console.WriteLine($"测试发送请求到[{urlCurrentUser}]");
                while (true)
                {
                    i++;
                    var response = client.GetAsync(urlCurrentUser).Result;
                    response.EnsureSuccessStatusCode();
                    var content = response.Content.ReadAsStringAsync().Result;
                    if (content != $"\"{user.UserName}\"")
                    {
                        errors++;
                        Console.Write($"{i} {client.DefaultRequestHeaders.Authorization} 获取到的信息有误：{content}");
                    }
                    else
                    {
                        Console.Write($"{i} {content}");
                    }
                    if (interactive)
                    {
                        Console.WriteLine("\t按任意键发送下一个请求，按q结束发送。");
                        var s = Console.ReadLine();
                        if (s == "Q" || s == "q")
                        {
                            break;
                        }
                    }
                    else
                    {
                        Console.WriteLine();
                        if (i >= 100)
                            break;
                        Thread.Sleep(100);
                    }
                }
                Console.WriteLine(errors == 0 ? $"所有信息完全匹配，按任意键继续..." : $"共{errors}次信息不匹配，按任意键继续...");
                Console.ReadLine();
            }
        }

        private static void TestAuthorization()
        {
            var user = SelectUser();
            Token token = null;
            if (user!=null)
            {
                var oldToken = TestLogin(user);
                token = TestRefreshToken(oldToken);
                var url = string.Format(urlGet, 0);
                Console.WriteLine($"测试以过期的令牌请求{url}");
                using(var client = CreateHttpClient(oldToken.AccessToken))
                {
                    var response = client.GetAsync(url).Result;
                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        Console.WriteLine($"返回状态：{response.StatusCode}，符合预期。");
                    }
                    else
                    {
                        Console.WriteLine($"返回状态与预期不符：{response.StatusCode}");
                    }
                }
            }
            Console.WriteLine($"以下使用刷新后的令牌请求：");
            using (var client = CreateHttpClient(token?.AccessToken))
            {
                Console.WriteLine($"API地址：{urlCurrentUser}");
                var response = client.GetAsync(urlCurrentUser).Result;
                var content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine($"返回数据：{content}");

                var url = string.Format(urlGet, user != null ? user.Id : 0);
                Console.WriteLine($"API地址：{url}");
                response = client.GetAsync(url).Result;
                Console.WriteLine($"返回状态：{response.StatusCode}");

                url = urlCreate;
                Console.WriteLine($"API地址：{url}");
                response = client.PostAsync(url, null).Result;
                content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine($"返回状态：{response.StatusCode}");

                url = urlUpdate;
                Console.WriteLine($"API地址：{url}");
                response = client.PostAsync(url, null).Result;
                content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine($"返回状态：{response.StatusCode}");

                url = urlPatch;
                Console.WriteLine($"API地址：{url}");
                response = client.PostAsync(url, null).Result;
                content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine($"返回状态：{response.StatusCode}");

                url = string.Format(urlDelete, new Random(Environment.TickCount).Next(100));
                Console.WriteLine($"API地址：{url}");
                response = client.DeleteAsync(url).Result;
                content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine($"返回状态：{response.StatusCode}");
            }
        }

        private static Token TestLogin(User user)
        {
            var jsonUser = Newtonsoft.Json.JsonConvert.SerializeObject(user);
            using (var client = CreateHttpClient())
            {
                var httpContent = new StringContent(jsonUser, Encoding.UTF8, "application/json");
                //下面这样设置媒体类型是错误的！！！
                //httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/josn");
                var response = client.PostAsync(urlLogin, httpContent).Result;
                response.EnsureSuccessStatusCode();
                var content = response.Content.ReadAsStringAsync().Result;
                var token = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(content);
                return token;
            }
        }

        private static Token TestRefreshToken(Token currentToken)
        {
            using (var client = CreateHttpClient(currentToken.AccessToken))
            {
                client.BaseAddress = new Uri(urlRoot);
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var response = client.PostAsync(string.Format(urlRefreshToken, currentToken.RefreshToken), null).Result;
                response.EnsureSuccessStatusCode();
                var content = response.Content.ReadAsStringAsync().Result;
                var token = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(content);
                return token;
            }
        }

        private static HttpClient CreateHttpClient(string authorization = null)
        {
            var client = new HttpClient();
            client.BaseAddress = new Uri(urlRoot);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            if (!string.IsNullOrEmpty(authorization))
                client.DefaultRequestHeaders.Add("authorization", authorization);
            return client;
        }

        private static User SelectUser()
        {
            var msg = "选择用户身份(按q键退出)：";
            var ids = new List<int>();
            foreach(var user in users)
            {
                ids.Add(user.Id);
                msg += $"\n{user.Id}) {user.UserName}";
            }
            while (true)
            {
                Console.WriteLine(msg);
                var s = Console.ReadLine();
                if (s == "Q" || s == "q")
                    return null;
                if(!int.TryParse(s , out int i) || !ids.Contains(i))
                {
                    Console.WriteLine("请输入正确的序号数字！");
                    continue;
                }
                return users[ids.IndexOf(i)];
            }
        }
    }

    public class Token
    {
        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }

    }

}
